#include <ptgui/ptgui.h>
#include <ptgui/font.h>
#include <ptgui/event.h>
#include <ptgui/widgets/widget.h>
#include <ptgui/widgets/container.h>

void _ptgui_widget_render(ptgui_widget_t *widget)
{
	ptgui_dc_t *dc;

	dc = ptgui_dc_begin_drawing(widget);

	if (widget->surface != NULL) {
		ptgui_surface_blit(widget->surface, dc, &widget->extent);
	}
	else {
		ptgui_dc_set_color(dc, COLOR_RED);
		ptgui_dc_draw_text(dc, "aaa", &widget->extent);
	}

	ptgui_dc_end_drawing(dc);
}

void _ptgui_widget_frame(ptgui_widget_t *widget, rt_uint32_t fDelta)
{
}

static void _ptgui_widget_constructor(ptgui_widget_t *widget)
{
	if (!widget) {
		return;
	}

	/* set default flag */
	widget->flag = PTGUI_WIDGET_FLAG_DEFAULT;

	/* init list */
	ptgui_list_init(&(widget->sibling));

	/* set parent and toplevel root */
	widget->parent = RT_NULL;

	/* set default event handler */
	widget->event_handler = ptgui_widget_event_handler;

	ptgui_rect_init(widget->extent);	

	widget->render_handler = _ptgui_widget_render;
	widget->frame_handler = _ptgui_widget_frame;
}

/* Destroys the widget */
static void _ptgui_widget_destructor(ptgui_widget_t *widget)
{
	if (widget == RT_NULL) {
		return;
	}

	if (widget->parent != RT_NULL) {
		/* remove widget from parent's children list */
		ptgui_list_remove(&(PTGUI_CONTAINER(widget->parent)->children), &(widget->sibling));

		widget->parent = RT_NULL;
	}
}

ptgui_type_t * ptgui_widget_type_get(void)
{
	static ptgui_type_t *widget_type = RT_NULL;

	if (!widget_type) {
		widget_type = ptgui_type_create("ptgui_widget", PTGUI_OBJECT_TYPE, sizeof(ptgui_widget_t),
										PTGUI_CONSTRUCTOR(_ptgui_widget_constructor),
										PTGUI_DESTRUCTOR(_ptgui_widget_destructor));
	}

	return widget_type;
}

ptgui_widget_t * ptgui_widget_create(ptgui_type_t *widget_type)
{
	struct ptgui_widget *widget;

	widget = PTGUI_WIDGET(ptgui_object_create(widget_type));

	return widget;
}

void ptgui_widget_destroy(ptgui_widget_t *widget)
{
	ptgui_object_destroy(PTGUI_OBJECT(widget));
}

void ptgui_widget_set_rect(ptgui_widget_t *widget, ptgui_rect_t *rect)
{
	if (widget == RT_NULL || rect == RT_NULL) {
		return;
	}

	widget->extent = *rect;
}

/*
 * This function moves widget and its children to a logic point
 */
void ptgui_widget_move_to_logic(ptgui_widget_t *widget, int dx, int dy)
{
	struct ptgui_list_node *node;
	ptgui_widget_t *child;

	if (widget == RT_NULL) {
		return;
	}

	ptgui_rect_moveto(&(widget->extent), dx, dy);

	/* move each child */
	if (PTGUI_IS_CONTAINER(widget)) {
		ptgui_list_foreach(node, &(PTGUI_CONTAINER(widget)->children))
		{
			child = ptgui_list_entry(node, ptgui_widget_t, sibling);

			ptgui_widget_move_to_logic(child, dx, dy);
		}
	}
}

void ptgui_widget_set_surface(ptgui_widget_t *widget, ptgui_surface_t *surface)
{
	widget->surface = surface;
}

void ptgui_widget_set_event_handler(ptgui_widget_t *widget, ptgui_event_handler_ptr handler)
{
	RT_ASSERT(widget != RT_NULL);

	widget->event_handler = handler;
}

void ptgui_widget_set_render_handler(ptgui_widget_t *widget, ptgui_render_handler_ptr handler)
{
	RT_ASSERT(widget != RT_NULL);

	widget->render_handler = handler;
}

void ptgui_widget_set_frame_handler(ptgui_widget_t *widget, ptgui_frame_handler_ptr handler)
{
	RT_ASSERT(widget != RT_NULL);

	widget->frame_handler = handler;
}

void ptgui_widget_get_rect(ptgui_widget_t *widget, ptgui_rect_t *rect)
{
	RT_ASSERT(widget != RT_NULL);

	if (rect != RT_NULL) {
		rect->x1 = rect->y1 = 0;
		rect->x2 = widget->extent.x2 - widget->extent.x1;
		rect->y2 = widget->extent.y2 - widget->extent.y1;
	}
}

/**
 * @brief Focuses the widget. The focused widget is the widget which can receive the keyboard events
 * @param widget a widget
 * @note The widget has to be attached to a toplevel widget, otherwise it will have no effect
 */
void ptgui_widget_focus(ptgui_widget_t *widget)
{
	ptgui_widget_t *focused;
	ptgui_container_t *parent;

	RT_ASSERT(widget != RT_NULL);

	if (!widget->parent || !PTGUI_WIDGET_IS_FOCUSABLE(widget) || !PTGUI_WIDGET_IS_ENABLE(widget)) {
		return;
	}

	/* set widget as focused */
	widget->flag |= PTGUI_WIDGET_FLAG_FOCUS;

	/* get parent container */
	parent = PTGUI_CONTAINER(widget->parent);

	/* get old focused widget */
	focused = parent->focused;
	if (focused == widget) {
		return ;
	} /* it's the same focused widget */

	if (focused != RT_NULL) {
		ptgui_widget_unfocus(focused);
	}

	/* set widget as focused widget in parent link */
	parent->focused = widget;
	while (PTGUI_WIDGET(parent)->parent != RT_NULL) {
		parent = PTGUI_CONTAINER(PTGUI_WIDGET(parent)->parent);
		parent->focused = widget;

		/* if the parent is hide, break it */
		if (PTGUI_WIDGET_IS_HIDE(PTGUI_WIDGET(parent))) {
			break;
		}
	}
}

/**
 * @brief Unfocused the widget
 * @param widget a widget
 */
void ptgui_widget_unfocus(ptgui_widget_t *widget)
{
	ptgui_widget_t *toplevel;
	RT_ASSERT(widget != RT_NULL);

	toplevel = ptgui_widget_get_toplevel(widget);
	if (!toplevel || !PTGUI_WIDGET_IS_FOCUSED(widget)) {
		return;
	}

	widget->flag &= ~PTGUI_WIDGET_FLAG_FOCUS;

	/* refresh widget */
	ptgui_widget_update(widget);
}

void ptgui_widget_point_to_device(ptgui_widget_t *widget, ptgui_point_t *point)
{
	RT_ASSERT(widget != RT_NULL);

	if (point != RT_NULL) {
		point->x += widget->extent.x1;
		point->y += widget->extent.y1;
	}
}

void ptgui_widget_rect_to_device(ptgui_widget_t *widget, ptgui_rect_t *rect)
{
	RT_ASSERT(widget != RT_NULL);

	if (rect != RT_NULL) {
		rect->x1 += widget->extent.x1;
		rect->x2 += widget->extent.x1;

		rect->y1 += widget->extent.y1;
		rect->y2 += widget->extent.y1;
	}
}

void ptgui_widget_point_to_logic(ptgui_widget_t *widget, ptgui_point_t *point)
{
	RT_ASSERT(widget != RT_NULL);

	if (point != RT_NULL) {
		point->x -= widget->extent.x1;
		point->y -= widget->extent.y1;
	}
}

void ptgui_widget_rect_to_logic(ptgui_widget_t *widget, ptgui_rect_t *rect)
{
	RT_ASSERT(widget != RT_NULL);

	if (rect != RT_NULL) {
		rect->x1 -= widget->extent.x1;
		rect->x2 -= widget->extent.x1;

		rect->y1 -= widget->extent.y1;
		rect->y2 -= widget->extent.y1;
	}
}

ptgui_widget_t * ptgui_widget_get_toplevel(ptgui_widget_t *widget)
{
	ptgui_widget_t *r;

	RT_ASSERT(widget != RT_NULL);

	r = widget;
	/* get the toplevel widget */
	while (r->parent != RT_NULL)
		r = r->parent;

	return r;
}

rt_bool_t ptgui_widget_event_handler(ptgui_widget_t *widget, ptgui_event_t *event)
{
	return RT_FALSE;
}

void ptgui_widget_show(ptgui_widget_t *widget)
{
	/* there is no parent or the parent is hide, no show at all */
	if (widget->parent == RT_NULL || PTGUI_WIDGET_IS_HIDE(widget->parent)) {
		return;
	}

	/* update the clip info of widget */
	PTGUI_WIDGET_UNHIDE(widget);
}

void ptgui_widget_hide(ptgui_widget_t *widget)
{
	/* hide this widget */
	PTGUI_WIDGET_HIDE(widget);
}

void ptgui_widget_update(ptgui_widget_t *widget)
{
	struct ptgui_event_paint paint;
	PTGUI_EVENT_PAINT_INIT(&paint);
	paint.wid = RT_NULL;

	RT_ASSERT(widget != RT_NULL);

	if (widget->event_handler != RT_NULL) {
		widget->event_handler(widget, &paint.parent);
	}
}

ptgui_widget_t * ptgui_widget_get_next_sibling(ptgui_widget_t *widget)
{
	ptgui_widget_t *sibling = RT_NULL;

	if (widget->sibling.next != RT_NULL) {
		sibling = ptgui_list_entry(widget->sibling.next, ptgui_widget_t, sibling);
	}

	return sibling;
}

ptgui_widget_t * ptgui_widget_get_prev_sibling(ptgui_widget_t *widget)
{
	struct ptgui_list_node *node;
	ptgui_widget_t *sibling,
					*parent;

	node = RT_NULL; sibling = RT_NULL;
	parent = widget->parent;
	if (parent != RT_NULL) {
		ptgui_list_foreach(node, &(PTGUI_CONTAINER(parent)->children))
		{
			if (node->next == &(widget->sibling)) {
				break;
			}
		}
	}

	if (node != RT_NULL) {
		sibling = ptgui_list_entry(node, ptgui_widget_t, sibling);
	}

	return sibling;
}